package com.xlongwei.info.util;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

import com.networknt.config.JsonMapper;
import com.networknt.httpstring.AttachmentConstants;
import com.networknt.utility.StringUtils;
import com.xlongwei.info.model.Info;
import com.xlongwei.info.model.User;

import io.undertow.Handlers;
import io.undertow.server.HttpServerExchange;
import io.undertow.server.handlers.sse.ServerSentEventConnection;
import io.undertow.server.handlers.sse.ServerSentEventConnectionCallback;
import io.undertow.server.handlers.sse.ServerSentEventHandler;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class Utils {

	public static final String dtp = "yyyy-MM-dd HH:mm";
	public static final DateTimeFormatter dtf = DateTimeFormatter.ofPattern(dtp);
	public static final Map<String, ServerSentEventConnection> conns = new ConcurrentHashMap<>();

	public static ServerSentEventHandler sse = Handlers.serverSentEvents(new ServerSentEventConnectionCallback() {
		@Override
		public void connected(ServerSentEventConnection connection, String lastEventId) {
			User user = user(connection);
			if (user != null) {
				String json = JsonMapper.toJson(user);
				log.info("connect user={}", json);
				if (user.isBan() == false) {
					conns.put(user.getUser(), connection);
					connection.send(json);
					Map<String, Object> map = new HashMap<>();
					map.put("places", InfoConfig.INSTANCE.getPlaces());
					map.put("tenant", InfoConfig.INSTANCE.getAdmin());
					json = JsonMapper.toJson(map);
					connection.send(json);
					Infos.infos().forEach(info -> {
						boolean match = Utils.match(info, user);
						String data = JsonMapper.toJson(info);
						log.info("match={} info={}", match, data);
						if (match) {
							connection.send(data);
						}
					});
					return;
				}
				log.info("ban user");
			}
		}
	});

	public static User user(ServerSentEventConnection connection) {
		String token = getParam(connection, "token");
		String username = username(token);
		return Users.user(username);
	}

	public static void notify(Info info) throws Exception {
		final String json = JsonMapper.toJson(info);
		log.info("notify info={}", json);
		conns.forEach((username, connection) -> {
			if (connection.isOpen() == false) {
				log.info("disconnect user={}", username);
				conns.remove(username);
				return;
			}
			User user = Users.user(username);
			boolean match = Utils.match(info, user);
			log.info("match={} user={}", match, JsonMapper.toJson(user));
			if (match) {
				connection.send(json);
			}
		});
	}

	public static String getParam(HttpServerExchange exchange, String name) {
		Deque<String> value = exchange.getQueryParameters().get(name);
		return StringUtils.trimToEmpty(value == null ? null : value.getFirst());
	}

	@SuppressWarnings("unchecked")
	public static Map<String, Object> getMap(HttpServerExchange exchange) {
		Object obj = exchange.getAttachment(AttachmentConstants.REQUEST_BODY);
		return obj instanceof Map ? (Map<String, Object>) obj : Collections.emptyMap();
	}

	public static String token(String username) {
		if (InfoConfig.INSTANCE.isRawToken()) {
			return username;
		}
		return String.valueOf(Long.rotateLeft(Long.parseLong(username), 1));
	}

	public static String username(String token) {
		if (InfoConfig.INSTANCE.isRawToken()) {
			return token;
		}
		try {
			return String.valueOf(Long.rotateLeft(Long.parseLong(token), -1));
		} catch (Exception e) {
			return StringUtils.EMPTY;
		}
	}

	/**
	 * 检查Info是否有效
	 * 
	 * @param info
	 * @return null有效，或错误提示
	 */
	public static String check(Info info) {
		User user = StringUtils.isBlank(info.getUser()) ? null : Users.user(info.getUser());
		if (user == null)
			return "没有登录";
		if (user.isBan()) {
			return "用户被禁";
		}
		if (user.isOwner() == false && info.isOwner())
			return "没有权限";
		if (StringUtils.isBlank(info.getFrom()) || InfoConfig.INSTANCE.getPlaces().contains(info.getFrom()) == false)
			return "出发有误";
		if (StringUtils.isBlank(info.getTo()) || info.getFrom().equals(info.getTo())
				|| InfoConfig.INSTANCE.getPlaces().contains(info.getTo()) == false)
			return "到达有误";
		int startGap = InfoConfig.INSTANCE.getStartGapMinutes();
		LocalDateTime start = LocalDateTime.now().plusMinutes(startGap), end = LocalDateTime.now().plusDays(8);
		if (info.getStart() == null || info.getEnd() == null || info.getStart().isBefore(start)
				|| info.getEnd().isBefore(info.getStart()) || info.getEnd().isAfter(end))
			return "时间有误";
		int endGap = InfoConfig.INSTANCE.getEndGapMinutes();
		if (info.getEnd().isBefore(info.getStart().plusMinutes(endGap))) {
			return "间隔太短";
		}
		if (StringUtils.isNotBlank(info.getNote()) && info.getNote().length() > 15) {
			return "备注有误";
		}
		return null;
	}

	/**
	 * 判断Info是否通知User
	 * 
	 * @param info
	 * @param user
	 * @return true通知 false忽略
	 */
	public static boolean match(Info info, User user) {
		if (user == null)
			return false;
		if (user.getUser().equals(info.getUser())) {
			return true;
		}
		if (info.isOwner() == false) {
			return user.isOwner();
		} else {
			return true;
		}
	}

	public static boolean toBoolean(Object object) {
		return Boolean.parseBoolean(Objects.toString(object));
	}

	public static String firstNotBlank(String... strings) {
		for (String string : strings) {
			if (StringUtils.isNotBlank(string)) {
				return string;
			}
		}
		return null;
	}

	public static LocalDateTime toLocalDateTime(Object object) {
		try {
			return LocalDateTime.parse(Objects.toString(object), dtf);
		} catch (Exception e) {
			return null;
		}
	}

	public static String getParam(ServerSentEventConnection connection, String name) {
		Deque<String> value = connection.getQueryParameters().get(name);
		return StringUtils.trimToEmpty(value == null ? null : value.getFirst());
	}
	
}
